﻿// Copyright (c) 2012 Mokah Nnamezie & Andrew Cooper
// Neo4Net graph database - http://neo4net.codeplex.com
//
// This file is part of Neo4Net - a .Net port of the Neo4j graph database
//
// Neo4Net is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

namespace Neo4Net.Kernel
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using Neo4Net.GraphDb;
using Neo4Net.Kernel.Impl.Util;
using Neo4Net.Kernel.Impl.Core;
using Neo4Net.GraphDb.Index;
using Neo4Net.Kernel.Impl.Transaction;
using Neo4Net.Kernel.Info;
using Neo4Net.Kernel.Impl.Cache;
using Neo4Net.Kernel.Impl.Persistence;
using System.IO;
    using Neo4Net.Kernel.Lifecycle;
    using Neo4Net.Kernel.Impl.Nioneo.Xa;
    using Neo4Net.Kernel.Configuration;
    using Neo4Net.Kernel.Impl.Transaction.XaFramework;
    using Neo4Net.Kernel.Logging;
    using Neo4Net.Kernel.Impl.Nioneo.Store;

    public abstract partial class AbstractGraphDatabase : IGraphDatabaseAPI, IGraphDatabaseService
    {
    //        private static readonly long MAX_NODE_ID = IdType.NODE.getMaxValue();
    //private static readonly long MAX_RELATIONSHIP_ID = IdType.RELATIONSHIP.getMaxValue();

    protected String storeDir;
    protected IDictionary<String, String> parameters;
    private IEnumerable<KernelExtension<object>> kernelExtensions;
    //protected StoreId storeId;
    private ITransaction placeboTransaction = null;
    private readonly ITransactionBuilder defaultTxBuilder;

    protected ILogging logging;
    protected StringLogger msgLog;
    //protected KernelEventHandlers kernelEventHandlers;
    //protected TransactionEventHandlers transactionEventHandlers;
    protected RelationshipTypeHolder relationshipTypeHolder;
    protected NodeManager nodeManager;
    protected IEnumerable<IIndexProvider> indexProviders;
    //protected IndexManagerImpl indexManager;
    protected Config config;
    protected KernelPanicEventGenerator kernelPanicEventGenerator;
    //protected TxHook txHook;
    //protected FileSystemAbstraction fileSystem;
    protected XaDataSourceManager xaDataSourceManager;
    //protected RagManager ragManager;
    protected LockManager lockManager;
    protected IIdGeneratorFactory idGeneratorFactory;
    //protected RelationshipTypeCreator relationshipTypeCreator;
    //protected LastCommittedTxIdSetter lastCommittedTxIdSetter;
    protected NioNeoDbPersistenceSource persistenceSource;
    //protected TxEventSyncHookFactory syncHook;
    //protected PersistenceManager persistenceManager;
    //protected PropertyIndexManager propertyIndexManager;
    protected LockReleaser lockReleaser;
    //protected IndexStore indexStore;
    //protected LogBufferFactory logBufferFactory;
    protected AbstractTransactionManager txManager;
    //protected TxIdGenerator txIdGenerator;
    //protected StoreFactory storeFactory;
    //protected XaFactory xaFactory;
    protected DiagnosticsManager diagnosticsManager;
    //protected NeoStoreXaDataSource neoDataSource;
    //protected RecoveryVerifier recoveryVerifier;
    protected Guard.Guard guard;

    //protected MeasureDoNothing monitorGc;

    //protected NodeAutoIndexerImpl nodeAutoIndexer;
    //protected RelationshipAutoIndexerImpl relAutoIndexer;
    protected KernelData extensions;
    //protected Caches caches;

    protected readonly LifeSupport life = new LifeSupport();
    private readonly IDictionary<String,CacheProvider> cacheProviders;

    protected AbstractGraphDatabase(String storeDir, IDictionary<string, string> parameters,
                                    IEnumerable<IIndexProvider> indexProviders, IEnumerable<KernelExtension<object>> kernelExtensions,
                                    IEnumerable<CacheProvider> cacheProviders)
    {
        this.defaultTxBuilder = new TransactionBuilderImpl(this, ForceMode.Forced);
        this.parameters = parameters;
        this.cacheProviders = MapCacheProviders( cacheProviders );
        this.storeDir = FileUtils.FixSeparatorsInPath( Canonicalize( storeDir ));

        // SPI - provided services
        this.indexProviders = indexProviders;
        this.kernelExtensions = kernelExtensions;
    }

    private IDictionary<String, CacheProvider> MapCacheProviders( IEnumerable<CacheProvider> cacheProviders )
    {
        IDictionary<String, CacheProvider> dictionary = new Dictionary<String, CacheProvider>();
        foreach (var provider in cacheProviders)
            dictionary.Add(provider.Name, provider);
        return dictionary;
    }


    protected void Run()
    {
        //Create();

        try
        {
            life.Start();
        }
        catch (LifecycleException ex)
        {
            msgLog.LogMessage("Startup failed", ex);

            Shutdown();

            // throw new IllegalStateException( "Startup failed", throwable );
            throw ex;
        }
    }

//    private void create()
//    {
//        // TODO THIS IS A SMELL - SHOULD BE AVAILABLE THROUGH OTHER MEANS!
//        String separator = System.getProperty( "file.separator" );
//        String store = this.storeDir + separator + NeoStore.DEFAULT_NAME;
//        params.put( Configuration.store_dir.name(), this.storeDir );
//        params.put( Configuration.neo_store.name(), store );
//        String logicalLog = this.storeDir + separator + NeoStoreXaDataSource.LOGICAL_LOG_DEFAULT_NAME;
//        params.put( Configuration.logical_log.name(), logicalLog );
//        // END SMELL

//        fileSystem = life.add(createFileSystemAbstraction());

//        // Get the list of settings classes for extensions
//        List<Class<?>> settingsClasses = new ArrayList<Class<?>>();
//        settingsClasses.add( GraphDatabaseSettings.class );
//        for( KernelExtension kernelExtension : kernelExtensions )
//        {
//            Class settingsClass = kernelExtension.getSettingsClass();
//            if( settingsClass != null )
//            {
//                settingsClasses.add( settingsClass );
//            }
//        }

//        // Apply defaults to configuration just for logging purposes
//        ConfigurationDefaults configurationDefaults = new ConfigurationDefaults( settingsClasses );

//        // Setup configuration
//        config = new Config( configurationDefaults.apply( new SystemPropertiesConfiguration(settingsClasses).apply( params ) ) );

//        // Create logger
//        this.logging = createStringLogger();

//        // Collect system properties, migrate settings and then apply defaults again
//        ConfigurationMigrator configurationMigrator = new ConfigurationMigrator( logging.getLogger( Loggers.CONFIG ) );
//        Map<String,String> configParams = configurationDefaults.apply(configurationMigrator.migrateConfiguration( new SystemPropertiesConfiguration(settingsClasses).apply( params )));

//        // Apply autoconfiguration for memory settings
//        AutoConfigurator autoConfigurator = new AutoConfigurator( fileSystem,
//                                                                  config.get( NeoStoreXaDataSource.Configuration.store_dir ),
//                                                                  config.getBoolean( GraphDatabaseSettings.use_memory_mapped_buffers ),
//                                                                  config.getBoolean( GraphDatabaseSettings.dump_configuration ) );
//        Map<String,String> autoConfiguration = autoConfigurator.configure( );
//        for( Map.Entry<String, String> autoConfig : autoConfiguration.entrySet() )
//        {
//            // Don't override explicit settings
//            if( !params.containsKey( autoConfig.getKey() ) )
//            {
//                configParams.put( autoConfig.getKey(), autoConfig.getValue() );
//            }
//        }

//        config.applyChanges( configParams );

//        this.msgLog = logging.getLogger( Loggers.NEO4J );

//        // Instantiate all services - some are overridable by subclasses
//        boolean readOnly = config.getBoolean( Configuration.read_only );

//        String cacheTypeName = config.get( Configuration.cache_type );
//        CacheProvider cacheProvider = cacheProviders.get( cacheTypeName );
//        if ( cacheProvider == null )
//            throw new IllegalArgumentException( "No cache type '" + cacheTypeName + "'" );

//        kernelEventHandlers = new KernelEventHandlers();

//        caches = createCaches();
//        diagnosticsManager = life.add(new DiagnosticsManager( logging.getLogger( Loggers.DIAGNOSTICS )) );

//        kernelPanicEventGenerator = new KernelPanicEventGenerator( kernelEventHandlers );

//        txHook = createTxHook();

//        xaDataSourceManager = life.add( new XaDataSourceManager( logging.getLogger( Loggers.DATASOURCE )) );

//        guard = config.getBoolean( Configuration.execution_guard_enabled ) ? new Guard( msgLog ) : null;

//        xaDataSourceManager = life.add(new XaDataSourceManager(msgLog));

//        if (readOnly)
//        {
//            txManager = new ReadOnlyTxManager(xaDataSourceManager);

//        } else
//        {
//            String serviceName = config.get( GraphDatabaseSettings.tx_manager_impl );
//            if ( serviceName == null )
//            {
//                txManager = new TxManager( this.storeDir, xaDataSourceManager, kernelPanicEventGenerator, txHook, logging.getLogger( Loggers.TXMANAGER ), fileSystem);
//            }
//            else {
//                TransactionManagerProvider provider;
//                provider = Service.load(TransactionManagerProvider.class, serviceName);
//                if ( provider == null )
//                {
//                    throw new IllegalStateException( "Unknown transaction manager implementation: "
//                            + serviceName );
//                }
//                txManager = provider.loadTransactionManager( this.storeDir, kernelPanicEventGenerator, txHook, logging.getLogger( Loggers.TXMANAGER ), fileSystem);
//            }
//        }
//        life.add( txManager );

//        transactionEventHandlers = new TransactionEventHandlers(txManager);

//        txIdGenerator = createTxIdGenerator();

//        ragManager = new RagManager(txManager );
//        lockManager = createLockManager();

//        idGeneratorFactory = createIdGeneratorFactory();

//        relationshipTypeCreator = createRelationshipTypeCreator();

//        lastCommittedTxIdSetter = createLastCommittedTxIdSetter();

//        persistenceSource = life.add(new NioNeoDbPersistenceSource(xaDataSourceManager));

//        syncHook = new DefaultTxEventSyncHookFactory();

//        // TODO Cyclic dependency! lockReleaser is null here
//        persistenceManager = new PersistenceManager(txManager,
//                persistenceSource, syncHook, lockReleaser );

//        propertyIndexManager = life.add(new PropertyIndexManager(
//                txManager, persistenceManager, persistenceSource));

//        lockReleaser = new LockReleaser(lockManager, txManager, nodeManager, propertyIndexManager);
//        persistenceManager.setLockReleaser(lockReleaser); // TODO This cyclic dep needs to be refactored

//        relationshipTypeHolder = new RelationshipTypeHolder( txManager,
//            persistenceManager, persistenceSource, relationshipTypeCreator );

//        caches.configure( cacheProvider, config );
//        Cache<NodeImpl> nodeCache = diagnosticsManager.tryAppendProvider( caches.node() );
//        Cache<RelationshipImpl> relCache = diagnosticsManager.tryAppendProvider( caches.relationship() );

//        nodeManager = guard != null ?
//                createGuardedNodeManager( readOnly, cacheProvider, nodeCache, relCache ) :
//                createNodeManager( readOnly, cacheProvider, nodeCache, relCache );

//        life.add( nodeManager );

//        lockReleaser.setNodeManager(nodeManager); // TODO Another cyclic dep that needs to be refactored

//        indexStore = new IndexStore( this.storeDir, fileSystem);

//        diagnosticsManager.prependProvider( config );

//        // Config can auto-configure memory mapping settings and what not, so reassign params
//        // after we've instantiated Config.
//        params = config.getParams();

//        /*
//         *  LogBufferFactory needs access to the parameters so it has to be added after the default and
//         *  user supplied configurations are consolidated
//         */

//        logBufferFactory = new DefaultLogBufferFactory();

//        extensions = life.add(createKernelData());

//        if ( config.getBoolean( Configuration.load_kernel_extensions ))
//        {
//            life.add(new DefaultKernelExtensionLoader( extensions ));
//        }

//        if( indexProviders == null )
//        {
//            indexProviders = new LegacyIndexIterable();
//        }
//        indexManager = new IndexManagerImpl(config, indexStore, xaDataSourceManager, txManager, this);
//        nodeAutoIndexer = life.add(new NodeAutoIndexerImpl( config, indexManager, nodeManager));
//        relAutoIndexer = life.add(new RelationshipAutoIndexerImpl( config, indexManager, nodeManager));

//        // TODO This cyclic dependency should be resolved
//        indexManager.setNodeAutoIndexer( nodeAutoIndexer );
//        indexManager.setRelAutoIndexer( relAutoIndexer );

//        recoveryVerifier = createRecoveryVerifier();

//        // Factories for things that needs to be created later
//        storeFactory = createStoreFactory();
//        xaFactory = new XaFactory(config, txIdGenerator, txManager, logBufferFactory, fileSystem, logging.getLogger( Loggers.XAFACTORY), recoveryVerifier );

//        // Create DataSource
//        List<Pair<TransactionInterceptorProvider, Object>> providers = new ArrayList<Pair<TransactionInterceptorProvider, Object>>( 2 );
//        for ( TransactionInterceptorProvider provider : Service.load( TransactionInterceptorProvider.class ) )
//        {
//            Object prov = params.get( TransactionInterceptorProvider.class.getSimpleName() + "." + provider.name() );
//            if ( prov != null )
//            {
//                providers.add( Pair.of( provider, prov ) );
//            }
//        }

//        try
//        {
//            // TODO IO stuff should be done in lifecycle. Refactor!
//            neoDataSource = new NeoStoreXaDataSource( config,
//                    storeFactory, fileSystem, lockManager, lockReleaser, logging.getLogger( Loggers.DATASOURCE ), xaFactory, providers, new DependencyResolverImpl());
//            xaDataSourceManager.registerDataSource( neoDataSource );
//        } catch (IOException e)
//        {
//            throw new IllegalStateException("Could not create Neo XA datasource", e);
//        }

//        life.add( new StuffToDoAfterRecovery() );

//        life.add( new MonitorGc( config, msgLog ) );

//        // This is how we lock the entire database to avoid threads using it during lifecycle events
//        life.add( new DatabaseAvailability() );

//        // Kernel event handlers should be the very last, i.e. very first to receive shutdown events
//        life.add( kernelEventHandlers );

//        // TODO This is probably too coarse-grained and we should have some strategy per user of config instead
//        life.add( new ConfigurationChangedRestarter() );
//    }

//    protected RelationshipTypeCreator createRelationshipTypeCreator()
//    {
//        return new DefaultRelationshipTypeCreator();
//    }

//    private NodeManager createNodeManager( final boolean readOnly, final CacheProvider cacheType,
//            Cache<NodeImpl> nodeCache, Cache<RelationshipImpl> relCache )
//    {
//        if ( readOnly )
//        {
//            return new ReadOnlyNodeManager( config, this, lockManager, lockReleaser, txManager, persistenceManager,
//                    persistenceSource, relationshipTypeHolder, cacheType, propertyIndexManager, createNodeLookup(),
//                    createRelationshipLookups(), nodeCache, relCache );
//        }

//        return new NodeManager( config, this, lockManager, lockReleaser, txManager, persistenceManager,
//                persistenceSource, relationshipTypeHolder, cacheType, propertyIndexManager, createNodeLookup(),
//                createRelationshipLookups(), nodeCache, relCache );
//    }

//    private NodeManager createGuardedNodeManager( final boolean readOnly, final CacheProvider cacheType,
//            Cache<NodeImpl> nodeCache, Cache<RelationshipImpl> relCache )
//    {
//        if ( readOnly )
//        {
//            return new ReadOnlyNodeManager( config, this, lockManager, lockReleaser, txManager, persistenceManager,
//                    persistenceSource, relationshipTypeHolder, cacheType, propertyIndexManager, createNodeLookup(),
//                    createRelationshipLookups(), nodeCache, relCache )
//            {
//                @Override
//                protected Node getNodeByIdOrNull( final long nodeId )
//                {
//                    guard.check();
//                    return super.getNodeByIdOrNull( nodeId );
//                }

//                @Override
//                public NodeImpl getNodeForProxy( final long nodeId, final LockType lock )
//                {
//                    guard.check();
//                    return super.getNodeForProxy( nodeId, lock );
//                }

//                @Override
//                public RelationshipImpl getRelationshipForProxy( final long relId, final LockType lock )
//                {
//                    guard.check();
//                    return super.getRelationshipForProxy( relId, lock );
//                }

//                @Override
//                protected Relationship getRelationshipByIdOrNull( final long relId )
//                {
//                    guard.check();
//                    return super.getRelationshipByIdOrNull( relId );
//                }

//                @Override
//                public Node createNode()
//                {
//                    guard.check();
//                    return super.createNode();
//                }

//                @Override
//                public Relationship createRelationship( final Node startNodeProxy, final NodeImpl startNode,
//                                                        final Node endNode, final RelationshipType type )
//                {
//                    guard.check();
//                    return super.createRelationship( startNodeProxy, startNode, endNode, type );
//                }
//            };
//        }

//        return new NodeManager( config, this, lockManager, lockReleaser, txManager, persistenceManager,
//                persistenceSource, relationshipTypeHolder, cacheType, propertyIndexManager, createNodeLookup(),
//                createRelationshipLookups(), nodeCache, relCache )
//        {
//            @Override
//            protected Node getNodeByIdOrNull( final long nodeId )
//            {
//                guard.check();
//                return super.getNodeByIdOrNull( nodeId );
//            }

//            @Override
//            public NodeImpl getNodeForProxy( final long nodeId, final LockType lock )
//            {
//                guard.check();
//                return super.getNodeForProxy( nodeId, lock );
//            }

//            @Override
//            public RelationshipImpl getRelationshipForProxy( final long relId, final LockType lock )
//            {
//                guard.check();
//                return super.getRelationshipForProxy( relId, lock );
//            }

//            @Override
//            protected Relationship getRelationshipByIdOrNull( final long relId )
//            {
//                guard.check();
//                return super.getRelationshipByIdOrNull( relId );
//            }

//            @Override
//            public Node createNode()
//            {
//                guard.check();
//                return super.createNode();
//            }

//            @Override
//            public Relationship createRelationship( final Node startNodeProxy, final NodeImpl startNode,
//                                                    final Node endNode, final RelationshipType type )
//            {
//                guard.check();
//                return super.createRelationship( startNodeProxy, startNode, endNode, type );
//            }
//        };
//    }


    
    public virtual void Shutdown()
    {
        try
        {
            life.Shutdown();
        }
        catch( LifecycleException ex )
        {
            msgLog.LogMessage( "Shutdown failed", ex );
        }
    }

    //protected StoreFactory createStoreFactory()
    //{
    //    return new StoreFactory(config, idGeneratorFactory, fileSystem, lastCommittedTxIdSetter, logging.getLogger( Loggers.NEOSTORE ), txHook);
    //}

    //protected RecoveryVerifier createRecoveryVerifier()
    //{
    //    return RecoveryVerifier.ALWAYS_VALID;
    //}

    //protected KernelData createKernelData()
    //{
    //    return new DefaultKernelData(config, this);
    //}

    //protected LastCommittedTxIdSetter createLastCommittedTxIdSetter()
    //{
    //    return new DefaultLastCommittedTxIdSetter();
    //}

    //protected TxIdGenerator createTxIdGenerator()
    //{
    //    return TxIdGenerator.DEFAULT;
    //}
    
    //protected Caches createCaches()
    //{
    //    return new DefaultCaches( msgLog );
    //}
    
    //protected RelationshipProxy.RelationshipLookups createRelationshipLookups()
    //{
    //    return new RelationshipProxy.RelationshipLookups()
    //    {
    //        @Override
    //        public Node lookupNode( long nodeId )
    //        {
    //            // TODO: add CAS check here for requests not in tx to guard against shutdown
    //            return nodeManager.getNodeById( nodeId );
    //        }

    //        @Override
    //        public RelationshipImpl lookupRelationship( long relationshipId )
    //        {
    //            // TODO: add CAS check here for requests not in tx to guard against shutdown
    //            return nodeManager.getRelationshipForProxy( relationshipId, null );
    //        }

    //        @Override
    //        public RelationshipImpl lookupRelationship( long relationshipId, LockType lock )
    //        {
    //            return nodeManager.getRelationshipForProxy( relationshipId, lock );
    //        }

    //        @Override
    //        public GraphDatabaseService getGraphDatabaseService()
    //        {
    //            return AbstractGraphDatabase.this;
    //        }

    //        @Override
    //        public NodeManager getNodeManager()
    //        {
    //            return nodeManager;
    //        }

    //        @Override
    //        public Node newNodeProxy( long nodeId )
    //        {
    //            // only used by relationship already checked as valid in cache
    //            return nodeManager.newNodeProxyById( nodeId );
    //        }
    //    };
    //}

    //protected NodeProxy.NodeLookup createNodeLookup()
    //{
    //    return new NodeProxy.NodeLookup()
    //    {
    //        @Override
    //        public NodeImpl lookup( long nodeId )
    //        {
    //            // TODO: add CAS check here for requests not in tx to guard against shutdown
    //            return nodeManager.getNodeForProxy( nodeId, null );
    //        }

    //        @Override
    //        public NodeImpl lookup( long nodeId, LockType lock )
    //        {
    //            return nodeManager.getNodeForProxy( nodeId, lock );
    //        }

    //        @Override
    //        public GraphDatabaseService getGraphDatabase()
    //        {
    //            // TODO This should be wrapped as well
    //            return AbstractGraphDatabase.this;
    //        }

    //        @Override
    //        public NodeManager getNodeManager()
    //        {
    //            return nodeManager;
    //        }
    //    };
    //}

    //protected TxHook createTxHook()
    //{
    //    return new DefaultTxHook();
    //}

    protected virtual IFileSystemAbstraction CreateFileSystemAbstraction()
    {
        throw new NotImplementedException();
        //return new DefaultFileSystemAbstraction();
    }

    protected virtual IIdGeneratorFactory CreateIdGeneratorFactory()
    {
        throw new NotImplementedException();
        //return new DefaultIdGeneratorFactory();
    }

    //protected LockManager createLockManager()
    //{
    //    return new LockManager(ragManager);
    //}

    protected virtual ILogging CreateStringLogger()
    {
        throw new NotImplementedException();
    //    try
    //    {
    //        getClass().getClassLoader().loadClass("ch.qos.logback.classic.LoggerContext");
    //        return life.add( new LogbackService( config ));
    //    }
    //    catch( ClassNotFoundException e )
    //    {
    //        return life.add( new ClassicLoggingService(config));
    //    }
    }

    public String StoreDir
    {
        get { return storeDir; }
    }

    //public StoreId getStoreId()
    //{
    //    return storeId;
    //}

    public ITransaction BeginTx()
    {
        return Tx.Begin();
    }

    ITransaction BeginTx( ForceMode forceMode )
    {
        throw new NotImplementedException();
    //    if ( transactionRunning() )
    //    {
    //        if ( placeboTransaction == null )
    //        {
    //            placeboTransaction = new PlaceboTransaction(
    //                    txManager );
    //        }
    //        return placeboTransaction;
    //    }
    //    Transaction result = null;
    //    try
    //    {
    //        txManager.begin( forceMode );
    //        result = new TopLevelTransaction( txManager, lockManager, lockReleaser );
    //    }
    //    catch ( Exception e )
    //    {
    //        throw new TransactionFailureException(
    //            "Unable to begin transaction", e );
    //    }
    //    return result;
    }

    //public bool TransactionRunning()
    //{
    //    try
    //    {
    //        return txManager.getTransaction() != null;
    //    }
    //    catch ( Exception e )
    //    {
    //        throw new TransactionFailureException(
    //                "Unable to get transaction.", e );
    //    }
    //}

    /**
     * Get a single management bean. Delegates to {@link #getSingleManagementBean(Class)}.
     *
     * @deprecated since Neo4j may now have multiple beans implementing the same bean interface, this method has been
     *             deprecated in favor of {@link #getSingleManagementBean(Class)} and {@link #getManagementBeans(Class)}
     *             . Version 1.5 of Neo4j will be the last version to contain this method.
     */
    //[Obsolete]
    //public final <T> T getManagementBean( Class<T> type )
    //{
    //    return getSingleManagementBean( type );
    //}

    //public final <T> T getSingleManagementBean( Class<T> type )
    //{
    //    Iterator<T> beans = getManagementBeans( type ).iterator();
    //    if ( beans.hasNext() )
    //    {
    //        T bean = beans.next();
    //        if( beans.hasNext() )
    //        {
    //            throw new NotFoundException( "More than one management bean for " + type.getName() );
    //        }
    //        return bean;
    //    }
    //    return null;
    //}

    protected virtual bool IsEphemeral
    {
        get { return false; }
    }

    public override string ToString()
    {
        return GetType().Name + " [" + StoreDir + "]";
    }

    //@Override
    //public Iterable<Node> getAllNodes()
    //{
    //    return GlobalGraphOperations.at( this ).getAllNodes();
    //}

    //@Override
    //public Iterable<RelationshipType> getRelationshipTypes()
    //{
    //    return GlobalGraphOperations.at( this ).getAllRelationshipTypes();
    //}

    //public KernelEventHandler registerKernelEventHandler(
    //        KernelEventHandler handler )
    //{
    //    return kernelEventHandlers.registerKernelEventHandler( handler );
    //}

    //public <T> TransactionEventHandler<T> registerTransactionEventHandler(
    //        TransactionEventHandler<T> handler )
    //{
    //    return transactionEventHandlers.registerTransactionEventHandler( handler );
    //}

    //public KernelEventHandler unregisterKernelEventHandler(
    //        KernelEventHandler handler )
    //{
    //    return kernelEventHandlers.unregisterKernelEventHandler( handler );
    //}

    //public <T> TransactionEventHandler<T> unregisterTransactionEventHandler(
    //        TransactionEventHandler<T> handler )
    //{
    //    return transactionEventHandlers.unregisterTransactionEventHandler( handler );
    //}

    public INode CreateNode()
    {
        return nodeManager.CreateNode();
    }

    //public Node getNodeById( long id )
    //{
    //    if ( id < 0 || id > MAX_NODE_ID )
    //    {
    //        throw new NotFoundException( "Node[" + id + "]" );
    //    }
    //    return nodeManager.getNodeById( id );
    //}

    public IRelationship GetRelationshipById( long id )
    {
        throw new NotImplementedException();
    //    if ( id < 0 || id > MAX_RELATIONSHIP_ID )
    //    {
    //        throw new NotFoundException( "Relationship[" + id + "]" );
    //    }
    //    return nodeManager.getRelationshipById( id );
    }

    public INode ReferenceNode
    {
        get { return nodeManager.GetReferenceNode(); }
    }

    public ITransactionBuilder Tx
    {
        get { return defaultTxBuilder; }
    }

    //@Override
    //public Guard getGuard()
    //{
    //    return guard;
    //}

    //public <T> Collection<T> getManagementBeans( Class<T> beanClass )
    //{
    //    KernelExtension<?> jmx = Service.load( KernelExtension.class, "kernel jmx" );
    //    if ( jmx != null )
    //    {
    //        Method getManagementBeans = null;
    //        Object state = jmx.getState( extensions );
    //        if ( state != null )
    //        {
    //            try
    //            {
    //                getManagementBeans = state.getClass().getMethod( "getManagementBeans", Class.class );
    //            }
    //            catch ( Exception e )
    //            {
    //                // getManagementBean will be null
    //            }
    //        }
    //        if ( getManagementBeans != null )
    //        {
    //            try
    //            {
    //                @SuppressWarnings( "unchecked" ) Collection<T> result =
    //                        (Collection<T>) getManagementBeans.invoke( state, beanClass );
    //                if( result == null )
    //                {
    //                    return Collections.emptySet();
    //                }
    //                return result;
    //            }
    //            catch ( InvocationTargetException ex )
    //            {
    //                Throwable cause = ex.getTargetException();
    //                if ( cause instanceof Error )
    //                {
    //                    throw (Error) cause;
    //                }
    //                if ( cause instanceof RuntimeException )
    //                {
    //                    throw (RuntimeException) cause;
    //                }
    //            }
    //            catch ( Exception ignored )
    //            {
    //                // exception thrown below
    //            }
    //        }
    //    }
    //    throw new UnsupportedOperationException( "Neo4j JMX support not enabled" );
    //}

    public KernelData KernelData
    {
        get { return extensions; }
    }

    //public IndexManager index()
    //{
    //    return indexManager;
    //}

    // GraphDatabaseSPI implementation - THESE SHOULD EVENTUALLY BE REMOVED! DON'T ADD dependencies on these!
    //public Config Config
    //{
    // get { return config; }
    //}

    public NodeManager NodeManager
    {
        get { return nodeManager; }
    }

    public LockReleaser LockReleaser
    {
        get { return lockReleaser; }
    }

    public LockManager LockManager 
    {
        get { return lockManager; }
    }

    public XaDataSourceManager XaDataSourceManager
    {
        get { return xaDataSourceManager; }
    }

    public Compat.Transaction.ITransactionManager TxManager
    {
        get { return txManager; } 
    }

    public RelationshipTypeHolder getRelationshipTypeHolder()
    {
        return relationshipTypeHolder;
    }

    public IIdGeneratorFactory IdGeneratorFactory
    {
        get { return idGeneratorFactory; }
    }

    public DiagnosticsManager DiagnosticsManager
    {
        get { return diagnosticsManager; }
    }

    public IPersistenceSource PersistenceSource
    {
         get { return persistenceSource; }
    }

    public StringLogger MessageLog
    {
        get { return msgLog; }
    }

    public KernelPanicEventGenerator KernelPanicGenerator
    {
        get { return kernelPanicEventGenerator; }
    }

    private String Canonicalize( String path )
    {
        return new FileInfo( path ).FullName;
    }

    //public boolean equals( Object o )
    //{
    //    if( this == o )
    //    {
    //        return true;
    //    }
    //    if ( o == null || !(o instanceof AbstractGraphDatabase) )
    //    {
    //        return false;
    //    }

    //    AbstractGraphDatabase that = (AbstractGraphDatabase) o;

    //    if( getStoreId() != null ? !getStoreId().equals( that.getStoreId() ) : that.getStoreId() != null )
    //    {
    //        return false;
    //    }
    //    if( !storeDir.equals( that.storeDir ) )
    //    {
    //        return false;
    //    }

    //    return true;
    //}

    public override int GetHashCode()
    {
        return storeDir.GetHashCode();
    }

    //protected class DefaultKernelData extends KernelData implements Lifecycle
    //{
    //    private final Config config;
    //    private final GraphDatabaseAPI graphDb;

    //    public DefaultKernelData(Config config, GraphDatabaseAPI graphDb)
    //    {
    //        this.config = config;
    //        this.graphDb = graphDb;
    //    }

    //    @Override
    //    public Version version()
    //    {
    //        return Version.getKernel();
    //    }

    //    @Override
    //    public Config getConfig()
    //    {
    //        return config;
    //    }

    //    @Override
    //    public Map<String, String> getConfigParams()
    //    {
    //        return config.getParams();
    //    }

    //    @Override
    //    public GraphDatabaseAPI graphDatabase()
    //    {
    //        return graphDb;
    //    }

    //    @Override
    //    public void init()
    //        throws Throwable
    //    {
    //    }

    //    @Override
    //    public void start()
    //        throws Throwable
    //    {
    //    }

    //    @Override
    //    public void stop()
    //        throws Throwable
    //    {
    //    }

    //    @Override
    //    public void shutdown()
    //        throws Throwable
    //    {
    //        // TODO This should be refactored so that shutdown does not need logger as input
    //        shutdown( msgLog );
    //    }
    //}

    //private class DefaultKernelExtensionLoader implements Lifecycle
    //{
    //    private final KernelData extensions;

    //    private Collection<KernelExtension<?>> loaded;

    //    public DefaultKernelExtensionLoader(KernelData extensions)
    //    {
    //        this.extensions = extensions;
    //    }

    //    @Override
    //    public void init()
    //        throws Throwable
    //    {
    //        loaded = extensions.loadExtensionConfigurations( logging.getLogger( Loggers.EXTENSION ), kernelExtensions );
    //        loadIndexImplementations(indexManager, logging.getLogger( Loggers.INDEX));
    //    }

    //    @Override
    //    public void start()
    //        throws Throwable
    //    {
    //        extensions.loadExtensions( loaded, logging.getLogger( Loggers.EXTENSION ) );
    //    }

    //    @Override
    //    public void stop()
    //        throws Throwable
    //    {
    //    }

    //    @Override
    //    public void shutdown()
    //        throws Throwable
    //    {
    //    }

    //    void loadIndexImplementations( IndexManagerImpl indexes, StringLogger msgLog )
    //    {
    //        for ( IndexProvider index : indexProviders)
    //        {
    //            try
    //            {
    //                indexes.addProvider( index.identifier(), index.load( new DependencyResolverImpl() ) );
    //            }
    //            catch ( Throwable cause )
    //            {
    //                msgLog.logMessage( "Failed to load index provider " + index.identifier(), cause );
    //                if( isAnUpgradeProblem( cause ) )
    //                {
    //                    throw launderedException( cause );
    //                }
    //                else
    //                {
    //                    cause.printStackTrace();
    //                }
    //            }
    //        }
    //    }

    //    private boolean isAnUpgradeProblem( Throwable cause )
    //    {
    //        while ( cause != null )
    //        {
    //            if( cause instanceof Throwable )
    //            {
    //                return true;
    //            }
    //            cause = cause.getCause();
    //        }
    //        return false;
    //    }

    //}

    //private class DefaultTxEventSyncHookFactory implements TxEventSyncHookFactory
    //{
    //    @Override
    //    public TransactionEventsSyncHook create()
    //    {
    //        return transactionEventHandlers.hasHandlers() ?
    //               new TransactionEventsSyncHook( nodeManager, transactionEventHandlers, txManager) : null;
    //    }
    //}

    ///**
    // * FIXME: This is supposed to be handled by a Dependency Injection framework...
    // * @author ceefour
    // */
    //class DependencyResolverImpl
    //        implements DependencyResolver
    //{
    //    @Override
    //    public <T> T resolveDependency(Class<T> type)
    //    {
    //        if( type.equals( Map.class ) )
    //        {
    //            return (T) getConfig().getParams();
    //        }
    //        else if( type.equals( Config.class ) )
    //        {
    //            return (T) getConfig();
    //        }
    //        else if( GraphDatabaseService.class.isAssignableFrom( type ) )
    //        {
    //            return (T) AbstractGraphDatabase.this;
    //        }
    //        else if( TransactionManager.class.isAssignableFrom( type ) )
    //        {
    //            return (T) txManager;
    //        }
    //        else if( LockManager.class.isAssignableFrom( type ) )
    //        {
    //            return (T) lockManager;
    //        }
    //        else if( LockReleaser.class.isAssignableFrom( type ) )
    //        {
    //            return (T) lockReleaser;
    //        }
    //        else if( StoreFactory.class.isAssignableFrom( type ) )
    //        {
    //            return (T) storeFactory;
    //        }
    //        else if( StringLogger.class.isAssignableFrom( type ) )
    //        {
    //            return (T) msgLog;
    //        }
    //        else if( IndexStore.class.isAssignableFrom( type ) )
    //        {
    //            return (T) indexStore;
    //        }
    //        else if( XaFactory.class.isAssignableFrom( type ) )
    //        {
    //            return (T) xaFactory;
    //        }
    //        else if( XaDataSourceManager.class.isAssignableFrom( type ) )
    //        {
    //            return (T) xaDataSourceManager;
    //        }
    //        else if( FileSystemAbstraction.class.isAssignableFrom( type ) )
    //        {
    //            return (T) fileSystem;
    //        }
    //        else if( Guard.class.isAssignableFrom( type ) )
    //        {
    //            return (T) guard;
    //        }
    //        else
    //        {
    //            throw new IllegalArgumentException( "Could not resolve dependency of type:" + type.getName() );
    //        }
    //    }
    //}

    //class DatabaseStartup
    //    implements Lifecycle
    //{
    //    @Override
    //    public void init()
    //        throws Throwable
    //    {
    //    }

    //    @Override
    //    public void start()
    //        throws Throwable
    //    {
    //    }

    //    @Override
    //    public void stop()
    //        throws Throwable
    //    {
    //    }

    //    @Override
    //    public void shutdown()
    //        throws Throwable
    //    {
    //    }
    //}

    ///**
    // * This class handles whether the database as a whole is available to use at all.
    // * As it runs as the last service in the lifecycle list, the stop() is called first
    // * on stop, shutdown or restart, and thus blocks access to everything else for outsiders.
    // */
    //class DatabaseAvailability
    //    implements Lifecycle
    //{
    //    @Override
    //    public void init()
    //        throws Throwable
    //    {
    //        // TODO: Starting database. Make sure none can access it through lock or CAS
    //    }

    //    @Override
    //    public void start()
    //        throws Throwable
    //    {
    //        // TODO: Starting database. Make sure none can access it through lock or CAS
    //        msgLog.logMessage( "Started - database is now available" );
    //    }

    //    @Override
    //    public void stop()
    //        throws Throwable
    //    {
    //        // TODO: Starting database. Make sure none can access it through lock or CAS
    //        msgLog.logMessage( "Stopping - database is now unavailable" );
    //    }

    //    @Override
    //    public void shutdown()
    //        throws Throwable
    //    {
    //        // TODO: Starting database. Make sure none can access it through lock or CAS
    //    }
    //}

    //// TODO Probably change name
    //class StuffToDoAfterRecovery implements Lifecycle
    //{
    //    @Override
    //    public void init() throws Throwable
    //    {
    //    }

    //    @Override
    //    public void start() throws Throwable
    //    {
    //        storeId = neoDataSource.getStoreId();
    //        KernelDiagnostics.register( diagnosticsManager, AbstractGraphDatabase.this,
    //                neoDataSource );
    //    }

    //    @Override
    //    public void stop() throws Throwable
    //    {
    //    }

    //    @Override
    //    public void shutdown() throws Throwable
    //    {
    //    }
    //}

    //private class ConfigurationChangedRestarter
    //    extends LifecycleAdapter
    //{
    //    private ConfigurationChangeListener listener = new ConfigurationChangeListener()
    //                {
    //                    Executor executor = Executors.newSingleThreadExecutor( new DaemonThreadFactory( "Database configuration restart" ) );

    //                    @Override
    //                    public void notifyConfigurationChanges( final Iterable<ConfigurationChange> change )
    //                    {
    //                        executor.execute( new Runnable()
    //                        {
    //                            @Override
    //                            public void run()
    //                            {
    //                                // Restart
    //                                try
    //                                {
    //                                    life.stop();
    //                                    life.start();

    //                                    msgLog.logMessage( "Database restarted with the following configuration changes:" + change );
    //                                }
    //                                catch( LifecycleException e )
    //                                {
    //                                    msgLog.logMessage( "Could not restart database", e );
    //                                }
    //                            }
    //                        } );
    //                    }
    //                };

    //    @Override
    //    public void start()
    //        throws Throwable
    //    {
    //        config.addConfigurationChangeListener( listener );
    //    }

    //    @Override
    //    public void stop()
    //        throws Throwable
    //    {
    //        config.removeConfigurationChangeListener( listener );
    //    }
    //}



    public INode GetNodeById(long node)
    {
        throw new NotImplementedException();
    }


    public RelationshipTypeHolder RelationshipTypeHolder
    {
        get { throw new NotImplementedException(); }
    }

    public T SingleManagementBean<T>(T type)
    {
        throw new NotImplementedException();
    }

    public ICollection<T> ManagementBeans<T>(T type)
    {
        throw new NotImplementedException();
    }

    public Guard.Guard Guard
    {
        get { throw new NotImplementedException(); }
    }
    }
}
